using System;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using QueryBuilder.Controls.Configuration;
using QueryBuilder.Core.Visualization.QueryVisualization;

namespace QueryBuilder.Controls
{
    /// <summary>
    /// 
    /// </summary>
    public class DataFieldInfoFactory : IVisualizableConstraintVisitor
    {
        private DataFieldInfo dataFieldInfo = new DataFieldInfo();
        private VisualizationConfig visualizationConfig;
        private ComparisonTemplateConfiguration comparisonTemplateConfiguration;

        /// <summary>
        /// Initializes a new instance of the <see cref="DataFieldInfoFactory"/> class.
        /// </summary>
        /// <param name="config">The config.</param>
        public DataFieldInfoFactory(VisualizationConfig config,ComparisonTemplateConfiguration comparisonTemplateConfiguration )
        {
            this.visualizationConfig = config;
            this.comparisonTemplateConfiguration = comparisonTemplateConfiguration;
        }

        public void Visit<T>(VisualizableVariable<T> visualizable)
        {
            string propertyBoundName = "Value";
            Type visualizationType = typeof (T);
            VisualizationMapping visualizationMapping = this.visualizationConfig.GetMapping(visualizationType);
            this.CreateDataFieldInfo(propertyBoundName, visualizationMapping.ViewerStyleKey, visualizationMapping.EditorStyleKey);
            this.dataFieldInfo.IsComplex = false;
        }

        public void Visit(VisualizableExpression visualizableExpression)
        {
            this.dataFieldInfo.DisplayControlFactory = () => visualizableExpression;
            this.dataFieldInfo.EditControlFactory = () => visualizableExpression;
            this.dataFieldInfo.IsComplex = true;
          
        }

        public void Visit(VisualizableCondition constraint)
        {
            throw new NotImplementedException();
        }

        private void CreateDataFieldInfo(string boundPropertyName, string viewerStyleKey, string editorStyleKey)
        {
            var twoWayBinding = new Binding(boundPropertyName)
                                    {
                                        Mode = BindingMode.TwoWay,
                                        ValidatesOnDataErrors = true,
                                        ValidatesOnExceptions = true,
                                    };

            object viewerStyle = Application.Current.Resources[viewerStyleKey];
            object editorStyle = Application.Current.Resources[editorStyleKey];
            if (viewerStyle != null)
            {
                this.dataFieldInfo.DisplayControlFactory = this.CreateFactory(twoWayBinding, (Style) viewerStyle);
            }
            if (editorStyle != null)
            {
                this.dataFieldInfo.EditControlFactory = this.CreateFactory(twoWayBinding, (Style)editorStyle);
            }
            this.dataFieldInfo.IsComplex = false;
        }

        public Func<object> CreateFactory(Binding binding, Style visualizationStyle)
        {
            return () =>
                       {
                           Style style = visualizationStyle; //(Style) value;

                           Type targetType = style.TargetType;
                           var frameworkElement = (FrameworkElement) Activator.CreateInstance(targetType);
                           frameworkElement.VerticalAlignment = VerticalAlignment.Center;
                           frameworkElement.HorizontalAlignment = HorizontalAlignment.Center;
                           frameworkElement.Style = style;
                           this.SetBinding(frameworkElement, binding);
                           return frameworkElement;
                       };
        }

        /// <summary>
        /// Sets the binding.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <param name="binding">The binding.</param>
        private void SetBinding(FrameworkElement frameworkElement, Binding binding)
        {
            var boundPropertyName = (string) frameworkElement.GetValue(Visualizer.BoundPropertyProperty);
            var converter = (IValueConverter) frameworkElement.GetValue(Visualizer.ConverterProperty);
            if (binding.Converter == null && converter != null)
            {
                binding.Converter = converter;
            }

            Type type = frameworkElement.GetType();

            DependencyProperty boundProperty = null;

            FieldInfo field = type.GetField(boundPropertyName + "Property", BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Static);

            if (null != field)
            {
                boundProperty = field.GetValue(null) as DependencyProperty;
            }

            if (boundProperty != null)
            {
                frameworkElement.SetBinding(boundProperty, binding);
            }
        }

        /// <summary>
        /// Gets the factory.
        /// </summary>
        /// <param name="visualizableConstraint">The constraint.</param>
        /// <returns></returns>
        public DataFieldInfo GetDataFieldInfo(VisualizableConstraint visualizableConstraint)
        {
            this.dataFieldInfo = new DataFieldInfo();
            this.dataFieldInfo.IsComplex = false;
           visualizableConstraint.Accept(this);
            return this.dataFieldInfo;
        }

        public DataFieldInfo GetComparisonInfo()
        {
            this.dataFieldInfo = new DataFieldInfo();
            string propertyBoundName = "Comparison";

            string viewerStyleKey = this.visualizationConfig.ComparisonViewerStyleKey;
            string editorStyleKey = this.visualizationConfig.ComparisonEditorStyleKey;

            this.CreateDataFieldInfo(propertyBoundName, viewerStyleKey, editorStyleKey);
            return this.dataFieldInfo;
        }
    }
}